package com.xxg.curd.impl;

import com.xxg.curd.CurdConfig;
import com.xxg.curd.SQLMap;
import com.xxg.curd.annotation.TableField;
import com.xxg.curd.annotation.TableName;
import com.xxg.curd.ex.CurdSQLException;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;

/**
 * 初始化映射关系
 *
 * 1.初始化数据库字段映射
 * 2.替换成用户自定义映射
 *
 * Created by game group on 2021/1/13.
 */
public abstract class BaseSQLMapImpl implements SQLMap {

    protected final DataSource source;
    protected Map<String,String> tableMap = new HashMap<>();
    protected Map<String,Map<String,String>> fieldMap = new HashMap<>();

    public BaseSQLMapImpl(CurdConfig config){
        this.source = config.getDataSource();

        initTableMap();
        initFieldMap();

        config.getMapAnnotation().forEach(this::fullMap);
    }

    protected void initTableMap() {
        resultHandler("show tables",res->{
            while (true) {
                try {
                    if (!res.next()) break;
                    String tableName = res.getString(1);
                    tableMap.put(tableName,tableName);
                } catch (SQLException e) {
                    throw new CurdSQLException(e);
                }
            }
        });
    }

    protected void initFieldMap() {
        tableMap.keySet().forEach(tableName ->
                resultHandler("DESC " + tableName,res ->{
                    Map<String,String> map = new HashMap<>();
                    while (true) {
                        try {
                            if (!res.next()) break;
                            String fieldName = res.getString(1);
                            map.put(fieldName,fieldName);
                        } catch (SQLException e) {
                            throw new CurdSQLException(e);
                        }
                    }
                    fieldMap.put(tableName,map);
                })
        );
    }

    protected void resultHandler(String sql, Consumer<ResultSet> consumer){
        try (Connection conn = source.getConnection();
             Statement st = conn.createStatement();
             ResultSet rs = st.executeQuery(sql)){
            consumer.accept(rs);
        } catch (SQLException e) {
            throw new CurdSQLException(e);
        }
    }


    /**
     * 获取映射关系
     * 类名与表名
     * 属性与字段
     * @param item
     */
    protected void fullMap(Class item) {
        TableName annotation = (TableName) item.getAnnotation(TableName.class);

        String value = annotation.value();
        String simpleName = item.getSimpleName();
        if(value == null || value.length() == 0) {
            value = simpleName;
        }

        Map<String,String> map = new HashMap<>();
        Field[] methods = item.getDeclaredFields();
        for (Field method : methods) {
            TableField tableField = method.getAnnotation(TableField.class);
            if(tableField != null){
                String fieldName = tableField.value();
                String name = method.getName();
                map.put(fieldName,name);
            }

        }

        Iterator<Map.Entry<String, String>> it = tableMap.entrySet().iterator();
        String key = null;
        while (it.hasNext()){
            Map.Entry<String, String> entry = it.next();
            if (entry.getValue().equals(value)) {
                key = entry.getKey();
                it.remove();
                break;
            }
        }

        tableMap.put(simpleName,value);

        if(key != null){
            Map<String, String> dbFieldMap = fieldMap.remove(key);
            if(dbFieldMap == null) dbFieldMap = new HashMap<>();

            dbFieldMap.values().forEach(dbFieldName -> {
                if (!map.containsKey(dbFieldName)) {
                    map.put(dbFieldName,dbFieldName);
                }
            });
        }

        fieldMap.put(simpleName,map);
    }
}
